using System.Linq;
using Charisma.Tests.Models.Persons;
using NUnit.Framework;

namespace Charisma.Tests.Linq
{
  [TestFixture]
  public class QueryTests
  {
    private PersonContext context;

    [SetUp]
    public void SetUp()
    {
      context = new PersonContext();
      context.Recreate();
    }

    [Test]
    public void SelectTest()
    {
      var persons = context.Persons.All.ToList();
      Assert.AreEqual(3, persons.Count);
      Assert.AreEqual(0, persons.Select(p => p.FirstName).Except(new[] {"Bob", "Jane", "Joe"}).ToList().Count);
    }

    [Test]
    public void SelectAnounymousTest()
    {
      var persons = context.Persons.All
        .Select(p => new {p.FirstName, p.LastName})
        .ToList();
      Assert.AreEqual(3, persons.Count);
      var expected = context.Persons.All
        .ToList()
        .Select(p => new {p.FirstName, p.LastName})
        .OrderBy(p => p.FirstName);
      Assert.IsTrue(expected.SequenceEqual(persons.OrderBy(p => p.FirstName)));
    }

    [Test]
    public void SelectAnonymousTest()
    {
      var persons = context.Persons.All
        .Select(p => new { p.FirstName, p.LastName })
        .ToList();
      Assert.AreEqual(3, persons.Count);
      var expected = context.Persons.All
        .ToList()
        .Select(p => new { p.FirstName, p.LastName })
        .OrderBy(p => p.FirstName);
      Assert.IsTrue(expected.SequenceEqual(persons.OrderBy(p => p.FirstName)));
    }

    [Test]
    public void SelectComputedTest()
    {
      var persons = context.Persons.All
        .Select(p => new { p.FirstName, p.LastName })
        .Select(x => x.FirstName + " " + x.LastName)
        .ToList();
      Assert.AreEqual(3, persons.Count);
      var expected = context.Persons.All
        .ToList()
        .Select(p => p.FirstName + " " +p.LastName)
        .OrderBy(p => p);
      Assert.IsTrue(expected.SequenceEqual(persons.OrderBy(p => p)));
    }

    [Test]
    public void SelectDeepTest()
    {
      var persons = context.Persons.All
        .Select(p => new { p.FirstName, p.LastName, p.PrimaryAddress.City })
        .ToList();
      Assert.AreEqual(3, persons.Count);
      var expected = context.Persons.All
        .ToList()
        .Select(p => new { p.FirstName, p.LastName, p.PrimaryAddress.City })
        .OrderBy(p => p.FirstName);
      Assert.IsTrue(expected.SequenceEqual(persons.OrderBy(p => p.FirstName)));
    }

    [Test]
    public void SelectPartialTest()
    {
      var persons = context.Persons.All
        .Select(p => new { p.FirstName })
        .ToList();
      Assert.AreEqual(3, persons.Count);
      var expected = context.Persons.All
        .ToList()
        .Select(p => new { p.FirstName })
        .OrderBy(p => p.FirstName);
      Assert.IsTrue(expected.SequenceEqual(persons.OrderBy(p => p.FirstName)));
    }

    [Test]
    public void SelectAnonymousEntityTest()
    {
      var persons = context.Persons.All
        .Select(p => new { Person = p, p.FirstName })
        .Select(x => new { X = x, x.Person.Age, x.FirstName})
        .ToList()
        .OrderBy(x => x.FirstName)
        .ToList();
      Assert.AreEqual(3, persons.Count);
      var expected = context.Persons.All
        .ToList()
        .Select(p => new { Person = p, p.FirstName, p.Age })
        .OrderBy(p => p.FirstName)
        .ToList();
      for (int i = 0; i < persons.Count; i++) {
        var person = persons[i];
        var expectedElement = expected[i];
        Assert.AreEqual(expectedElement.FirstName, person.FirstName);
        Assert.AreEqual(expectedElement.Age, person.Age);
        Assert.AreEqual(expectedElement.Person.Id, person.X.Person.Id);
        Assert.AreEqual(expectedElement.Person.FirstName, person.X.Person.FirstName);
        Assert.AreEqual(expectedElement.Person.LastName, person.X.Person.LastName);
        Assert.IsNotNull(person.X.Person.PrimaryAddress);
        Assert.AreEqual(expectedElement.Person.PrimaryAddress.City, person.X.Person.PrimaryAddress.City);
      }
    }


    [Test]
    public void WhereBooleanTest()
    {
      var persons = context.Persons.All
        .Where(p => p.PrimaryAddress.IsInternational)
        .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereBoolean2Test()
    {
      var persons = context.Persons.All
        .Where(p => !p.PrimaryAddress.IsInternational)
        .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereCalculatedTest()
    {
      var persons = context.Persons.All
        .Where(p => p.Age + 23 < 50)
        .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereCalculatedProjectionTest()
    {
      var persons = context.Persons.All
        .Select(p => new { Name = p.FirstName + " "+ p.LastName, p.Age })
        .Where(x => x.Age > 21)
        .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereCalculatedProjection2Test()
    {
      var persons = context.Persons.All
        .Select(p => new { Name = p.FirstName + " " + p.LastName, Age = p.Age + 23 })
        .Where(x => x.Age < 50)
        .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereCalculatedProjection3Test()
    {
      var persons = context.Persons.All
        .Select(p => new { Name = p.FirstName + " " + p.LastName, p.Age })
        .Where(x => x.Age > 21)
        .Select(x => x.Name)
        .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereDisjunctionTest()
    {
      var persons = context.Persons.All
       .Where(p => p.Age == 21 || p.Age == 35)
       .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereDisjunction2Test()
    {
      var persons = context.Persons.All
       .Where(p => p.Age == 21 || p.Age == 35 || p.Age == 33)
       .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereDisjunction3Test()
    {
      var persons = context.Persons.All
       .Where(p => p.Age >= 100 || p.Age < 10 || (p.Age >= 33 || p.Age == 13))
       .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereDisjunction4Test()
    {
      var persons = context.Persons.All
       .Where(p => (p.Age >= 30 && p.Age < 38) || (p.Age >= 40 && p.Age <= 100))
       .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereNotDisjunctionTest()
    {
      var persons = context.Persons.All
       .Where(p => !(p.Age == 21 || p.Age == 35))
       .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereNotDisjunction2Test()
    {
      var persons = context.Persons.All
       .Where(p => !(p.Age == 21 || p.Age == 35 || p.Age == 33))
       .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereConjunctionTest()
    {
      var persons = context.Persons.All
       .Where(p => p.Age > 21 && p.Age < 42)
       .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereNotConjunctionTest()
    {
      var persons = context.Persons.All
       .Where(p => !(p.Age > 21 && p.Age < 42))
       .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereNotConjunction2Test()
    {
      var persons = context.Persons.All
       .Where(p => !!(p.Age > 21 && p.Age < 42))
       .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereLocalParameterTest()
    {
      var age = 21;
      var persons = context.Persons.All
       .Where(p => p.Age > age)
       .ToList();
        Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereLocalReferenceParameterTest()
    {
      var @ref = new {Actual = new {Age = 21}};
      var persons = context.Persons.All
       .Where(p => p.Age > @ref.Actual.Age)
       .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereEnumTest()
    {
      var persons = context.Persons.All
        .Where(p => p.PrimaryAddress.AddressType == AddressType.Company)
        .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereNullTest()
    {
      var persons = context.Persons.All
        .Where(p => p.MidName == null)
        .ToList();
      Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereNotNullTest()
    {
      var persons = context.Persons.All
        .Where(p => p.MidName != null)
        .ToList();
      Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void WhereNullClosureTest()
    {
        string empty = null;
        var persons = context.Persons.All
          .Where(p => p.MidName == empty)
          .ToList();
        Assert.AreEqual(2, persons.Count);
    }

    [Test]
    public void WhereNotNullClosureTest()
    {
        string empty = null;
        var persons = context.Persons.All
          .Where(p => p.MidName != empty)
          .ToList();
        Assert.AreEqual(1, persons.Count);
    }

    [Test]
    public void CountTest()
    {
      var count = context.Persons.All.Count();
      Assert.AreEqual(3, count);
    }

    [Test]
    public void CountPredicateTest()
    {
      var count = context.Persons.All.Count(p => p.Age > 21);
      Assert.AreEqual(2, count);
    }

    [Test]
    public void CountPredicate2Test()
    {
      var count = context.Persons.All
        .Where(p => p.Age > 21)
        .Count();
      Assert.AreEqual(2, count);
    }



  /*
            [Test]
            public void DocumentQuery()
            {
                var people = (from p in DocumentCollection.Linq()
                              where p.Key("age") > 21
                              select (string)p["fn"]).ToList();

                Assert.AreEqual(2, people.Count);
            }

        
            [Test]
            public void First()
            {
                var person = Collection.Linq().OrderBy(x => x.Age).First();

                Assert.AreEqual("Joe", person.FirstName);
            }

            [Test]
            public void LocalEnumerable_Contains()
            {
                var names = new[] { "Joe", "Bob" };
                var people = Collection.Linq().Where(x => names.Contains(x.FirstName)).ToList();

                Assert.AreEqual(2, people.Count);
            }

            [Test]
            public void LocalList_Contains()
            {
                var names = new List<string> { "Joe", "Bob" };
                var people = Collection.Linq().Where(x => names.Contains(x.FirstName)).ToList();

                Assert.AreEqual(2, people.Count);
            }

            [Test]
            public void NestedArray_Length()
            {
                var people = (from p in Collection.Linq()
                              where p.EmployerIds.Length == 1
                              select p).ToList();

                Assert.AreEqual(2, people.Count);
            }

            [Test(Description = "This will fail < 1.4")]
            public void NestedArray_indexer()
            {
                var people = Collection.Linq().Where(x => x.EmployerIds[0] == 1).ToList();

                Assert.AreEqual(2, people.Count);
            }

           [Test]
            public void NestedCollection_Count()
            {
                var people = (from p in Collection.Linq()
                              where p.Addresses.Count == 1
                              select p).ToList();

                Assert.AreEqual(1, people.Count);
            }

            [Test(Description = "This will fail < 1.4")]
            public void NestedList_indexer()
            {
                var people = Collection.Linq().Where(x => x.Addresses[1].City == "Tokyo").ToList();

                Assert.AreEqual(1, people.Count);
            }

            [Test]
            public void NestedQueryable_Any()
            {
                var people = Collection.Linq().Where(x => x.Addresses.Any(a => a.City == "London")).ToList();

                Assert.AreEqual(2, people.Count);
            }

            [Test]
            public void NestedQueryable_Contains()
            {
                var people = Collection.Linq().Where(x => x.EmployerIds.Contains(1)).ToList();

                Assert.AreEqual(2, people.Count);
            }

            [Test]
            public void Nested_Queryable_Count()
            {
                var people = Collection.Linq().Where(x => x.Addresses.Count() == 1).ToList();

                Assert.AreEqual(1, people.Count);
            }

            [Test(Description = "This will fail < 1.4")]
            public void Nested_Queryable_ElementAt()
            {
                var people = Collection.Linq().Where(x => x.Addresses.ElementAt(1).City == "Tokyo").ToList();

                Assert.AreEqual(1, people.Count);
            }

           
            [Test]
            public void OrderBy()
            {
                var people = Collection.Linq().OrderBy(x => x.Age).ThenByDescending(x => x.LastName).ToList();

                Assert.AreEqual("Joe", people[0].FirstName);
                Assert.AreEqual("Jane", people[1].FirstName);
                Assert.AreEqual("Bob", people[2].FirstName);
            }
     [Test]
        public void ProjectionWithConstraints()
        {
            var people = (from p in Collection.Linq()
                          where p.Age > 21 && p.Age < 42
                          select new { Name = p.FirstName + p.LastName }).ToList();

            Assert.AreEqual(1, people.Count);
        }

        [Test]
        public void Regex_IsMatch()
        {
            var people = (from p in Collection.Linq()
                          where Regex.IsMatch(p.FirstName, "Joe")
                          select p).ToList();

            Assert.AreEqual(1, people.Count);
        }

        [Test]
        public void Single()
        {
            var person = Collection.Linq().Where(x => x.Age == 21).Single();

            Assert.AreEqual("Joe", person.FirstName);
        }

        [Test]
        public void SingleEqualConstraint()
        {
            var people = Collection.Linq().Where(p => "Joe" == p.FirstName).ToList();

            Assert.AreEqual(1, people.Count);
        }

        [Test]
        public void SkipAndTake()
        {
            var people = Collection.Linq().OrderBy(x => x.Age).Skip(2).Take(1).ToList();

            Assert.AreEqual("Bob", people[0].FirstName);
        }

        [Test]
        public void String_Contains()
        {
            var people = (from p in Collection.Linq()
                          where p.FirstName.Contains("o")
                          select p).ToList();

            Assert.AreEqual(2, people.Count);
        }

        [Test]
        public void String_EndsWith()
        {
            var people = (from p in Collection.Linq()
                          where p.FirstName.EndsWith("e")
                          select p).ToList();

            Assert.AreEqual(2, people.Count);
        }

        [Test]
        public void String_StartsWith()
        {
            var people = (from p in Collection.Linq()
                          where p.FirstName.StartsWith("J")
                          select p).ToList();

            Assert.AreEqual(2, people.Count);
        }

       
        }*/
  }
}